package org.jetbrains.desktop.sample.macos

import org.jetbrains.desktop.macos.AppMenuItem
import org.jetbrains.desktop.macos.AppMenuManager
import org.jetbrains.desktop.macos.AppMenuStructure
import org.jetbrains.desktop.macos.Application
import org.jetbrains.desktop.macos.Cursor
import org.jetbrains.desktop.macos.DragAndDropCallbacks
import org.jetbrains.desktop.macos.DragAndDropHandler
import org.jetbrains.desktop.macos.DragInfo
import org.jetbrains.desktop.macos.DragOperation
import org.jetbrains.desktop.macos.Event
import org.jetbrains.desktop.macos.EventHandlerResult
import org.jetbrains.desktop.macos.FileDialog
import org.jetbrains.desktop.macos.GrandCentralDispatch
import org.jetbrains.desktop.macos.KeyModifiersSet
import org.jetbrains.desktop.macos.Keystroke
import org.jetbrains.desktop.macos.KotlinDesktopToolkit
import org.jetbrains.desktop.macos.LogLevel
import org.jetbrains.desktop.macos.Logger
import org.jetbrains.desktop.macos.LogicalPixels
import org.jetbrains.desktop.macos.LogicalPoint
import org.jetbrains.desktop.macos.LogicalSize
import org.jetbrains.desktop.macos.MetalCommandQueue
import org.jetbrains.desktop.macos.MetalDevice
import org.jetbrains.desktop.macos.NotificationAction
import org.jetbrains.desktop.macos.NotificationCategory
import org.jetbrains.desktop.macos.NotificationCenter
import org.jetbrains.desktop.macos.NotificationSound
import org.jetbrains.desktop.macos.Pasteboard
import org.jetbrains.desktop.macos.PhysicalPoint
import org.jetbrains.desktop.macos.PhysicalSize
import org.jetbrains.desktop.macos.Screen
import org.jetbrains.desktop.macos.TitlebarConfiguration
import org.jetbrains.desktop.macos.Window
import org.jetbrains.desktop.macos.WindowBackground
import org.jetbrains.desktop.macos.WindowEvent
import org.jetbrains.desktop.macos.WindowId
import org.jetbrains.desktop.macos.WindowVisualEffect
import org.jetbrains.desktop.sample.common.runtimeInfo
import org.jetbrains.skia.Canvas
import org.jetbrains.skia.Color
import org.jetbrains.skia.Paint
import org.jetbrains.skia.Rect
import java.lang.AutoCloseable
import java.lang.Thread.sleep
import kotlin.concurrent.Volatile
import kotlin.concurrent.thread
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin

class CustomTitlebar(
    private var origin: LogicalPoint,
    var size: LogicalSize,
    var startWindowDrag: (() -> Unit)? = null,
    var zoomBoxClicked: (() -> Unit)? = null,
) {
    companion object {
        const val DEFAULT_CUSTOM_TITLEBAR_HEIGHT: LogicalPixels = 55.0
    }

    private val colors = intArrayOf(
        Color.RED,
        Color.YELLOW,
        Color.GREEN,
    )
    private var currentColorIndex = 0
    private var totalClicks = 0

    private fun isPointInSquare(point: LogicalPoint): Boolean {
        val squareSize = size.height
        val squareX = origin.x + (size.width - squareSize) / 2
        return point.x >= squareX &&
            point.x <= squareX + squareSize &&
            point.y >= origin.y &&
            point.y <= origin.y + size.height
    }

    fun handleEvent(event: Event): EventHandlerResult {
        return when (event) {
            is Event.MouseDown -> {
                if (isPointInSquare(event.locationInWindow)) {
                    currentColorIndex = (currentColorIndex + 1) % colors.size
                    totalClicks++
                    Logger.info { "Square clicked $totalClicks times total" }
                    EventHandlerResult.Stop
                } else if (event.locationInWindow.x > origin.x &&
                    event.locationInWindow.x < origin.x + size.width * 0.75 &&
                    event.locationInWindow.y > origin.y &&
                    event.locationInWindow.y < origin.y + size.height
                ) {
                    if (event.clickCount == 1L) {
                        startWindowDrag?.invoke()
                    } else {
                        zoomBoxClicked?.invoke()
                    }

                    EventHandlerResult.Stop
                } else {
                    EventHandlerResult.Continue
                }
            }

            else -> EventHandlerResult.Continue
        }
    }

    fun draw(canvas: Canvas, time: Long, scale: Double) {
        val physicalOrigin = origin.toPhysical(scale)
        val physicalSize = size.toPhysical(scale)
        val x = physicalOrigin.x.toFloat()
        val y = physicalOrigin.y.toFloat()
        val width = physicalSize.width.toFloat()
        val height = physicalSize.height.toFloat()
        Paint().use { paint ->
            paint.color = 0xFF404040.toInt()
            canvas.drawRect(Rect.makeXYWH(x, y, width, height), paint)
        }
        Paint().use { paint ->
            paint.color = 0xFFAAAAAA.toInt()
            canvas.drawRect(Rect.makeXYWH(width * 0.75f, y, width * 0.25f, height), paint)
        }

        // Draw the color-changing square
        Paint().use { paint ->
            paint.color = colors[currentColorIndex]
            val squareSize = size.height
            val squarePhysicalX = (origin.x + (size.width - squareSize) / 2) * scale
            val squarePhysicalY = origin.y * scale
            val squarePhysicalSize = squareSize * scale
            canvas.drawRect(
                Rect.makeXYWH(
                    squarePhysicalX.toFloat(),
                    squarePhysicalY.toFloat(),
                    squarePhysicalSize.toFloat(),
                    squarePhysicalSize.toFloat(),
                ),
                paint,
            )
        }
    }
}

class ContentArea(
    var origin: LogicalPoint,
    var size: LogicalSize,
) {

    @Volatile
    private var markerPosition: LogicalPoint? = null

    fun handleEvent(event: Event): EventHandlerResult {
        return when (event) {
            is Event.MouseMoved -> {
                markerPosition = LogicalPoint(
                    event.locationInWindow.x - origin.x,
                    event.locationInWindow.y - origin.y,
                )
                EventHandlerResult.Continue
            }

            else -> EventHandlerResult.Continue
        }
    }

    fun draw(canvas: Canvas, time: Long, scale: Double) {
        val contentOrigin = origin.toPhysical(scale)
        val contentSize = size.toPhysical(scale)

        Paint().use { paint ->
            paint.color = 0x77264653
            canvas.drawRect(
                Rect.makeXYWH(
                    contentOrigin.x.toFloat(),
                    contentOrigin.y.toFloat(),
                    contentSize.width.toFloat(),
                    contentSize.height.toFloat(),
                ),
                paint,
            )
        }
        canvas.drawSpiningCircle(contentOrigin, contentSize, time, scale.toFloat())
        canvas.drawWindowBorders(contentOrigin, contentSize, time, scale.toFloat())
        canvas.drawCursor(contentOrigin, contentSize, time, scale.toFloat())
    }

    private fun Canvas.drawSpiningCircle(origin: PhysicalPoint, size: PhysicalSize, t: Long, scale: Float) = withTranslated(origin) {
        val width = size.width.toFloat()
        val height = size.height.toFloat()
        val angle = (t / 2000f) * 2f * PI
        val r = width / 4
        val x = r * sin(angle).toFloat() + width / 2f
        val y = r * cos(angle).toFloat() + height / 2f
        Paint().use { paint ->
            paint.color = Color.GREEN
            drawCircle(x, y, 30f, paint)
        }
    }

    private fun Canvas.drawWindowBorders(origin: PhysicalPoint, size: PhysicalSize, t: Long, scale: Float) = withTranslated(origin) {
        val width = size.width.toFloat()
        val height = size.height.toFloat()
        Paint().use { paint ->
            val barSize = 3 * scale

            // left
            paint.color = 0xFFe76f51.toInt()
            drawRect(Rect.makeXYWH(0f, 0f, barSize, 100 * scale), paint)
            drawRect(Rect.makeXYWH(0f, height / 2 - 50 * scale, barSize, 100 * scale), paint)
            drawRect(Rect.makeXYWH(0f, height - 100 * scale, barSize, 100 * scale), paint)

            // top
            paint.color = 0xFF2a9d8f.toInt()
            drawRect(Rect.makeXYWH(0f, 0f, 100 * scale, barSize), paint)
            drawRect(Rect.makeXYWH(width / 2 - 50 * scale, 0f, 100 * scale, barSize), paint)
            drawRect(Rect.makeXYWH(width - 100 * scale, 0f, 100 * scale, barSize), paint)

            // right
            paint.color = 0xFFe9c46a.toInt()
            drawRect(Rect.makeXYWH(width - barSize, 0f, barSize, 100 * scale), paint)
            drawRect(Rect.makeXYWH(width - barSize, height / 2 - 50 * scale, barSize, 100 * scale), paint)
            drawRect(Rect.makeXYWH(width - barSize, height - 100 * scale, barSize, 100 * scale), paint)

            // bottom
            paint.color = 0xFFFFFFFF.toInt()
            drawRect(Rect.makeXYWH(0f, height - barSize, 100 * scale, barSize), paint)
            drawRect(Rect.makeXYWH(width / 2 - 50 * scale, height - barSize, 100 * scale, barSize), paint)
            drawRect(Rect.makeXYWH(width - 100 * scale, height - barSize, 100 * scale, barSize), paint)
        }
    }

    private fun Canvas.drawCursor(origin: PhysicalPoint, size: PhysicalSize, t: Long, scale: Float) = withTranslated(origin) {
        val canvas = this
        markerPosition?.let { curs ->
            val positive = curs.x > 0 && curs.y > 0
            val inBox = curs.x < size.width && curs.y < size.height
            if (positive && inBox) {
                val x = curs.x.toFloat()
                val y = curs.y.toFloat()
                val width = size.width.toFloat()
                val height = size.height.toFloat()

                Paint().use { paint ->
                    paint.color = 0x40FFFFFF
                    canvas.drawRect(Rect.makeXYWH(0f, y * scale, width, 2 * scale), paint)
                    canvas.drawRect(Rect.makeXYWH(x * scale, 0f, 2 * scale, height), paint)
                }
            }
        }
    }
}

class WindowContainer(
    var customTitlebar: CustomTitlebar?,
    private val contentArea: ContentArea,
) {
    companion object {
        fun create(windowContentSize: LogicalSize, useCustomTitlebar: Boolean): WindowContainer {
            return if (useCustomTitlebar) {
                val titlebar = CustomTitlebar(
                    LogicalPoint.Zero,
                    LogicalSize(width = windowContentSize.width, height = CustomTitlebar.DEFAULT_CUSTOM_TITLEBAR_HEIGHT),
                )
                val contentArea = ContentArea(
                    LogicalPoint(x = 0.0, y = titlebar.size.height),
                    LogicalSize(
                        width = windowContentSize.width,
                        height = windowContentSize.height - titlebar.size.height,
                    ),
                )
                WindowContainer(titlebar, contentArea)
            } else {
                val contentArea = ContentArea(LogicalPoint.Zero, windowContentSize)
                WindowContainer(null, contentArea)
            }
        }
    }

    fun resize(windowSize: LogicalSize) {
        val customTitlebar = customTitlebar
        if (customTitlebar != null) {
            customTitlebar.size = LogicalSize(width = windowSize.width, height = customTitlebar.size.height)
            contentArea.origin = LogicalPoint(x = 0.0, y = customTitlebar.size.height)
            contentArea.size =
                LogicalSize(width = windowSize.width, height = windowSize.height - customTitlebar.size.height)
        } else {
            contentArea.origin = LogicalPoint.Zero
            contentArea.size = windowSize
        }
    }

    fun handleEvent(event: Event): EventHandlerResult {
        return when {
            customTitlebar?.handleEvent(event) == EventHandlerResult.Stop -> EventHandlerResult.Stop
            contentArea.handleEvent(event) == EventHandlerResult.Stop -> EventHandlerResult.Stop
            else -> EventHandlerResult.Continue
        }
    }

    fun draw(canvas: Canvas, time: Long, scale: Double) {
        customTitlebar?.draw(canvas, time, scale)
        contentArea.draw(canvas, time, scale)
    }
}

class RotatingBallWindow(
    device: MetalDevice,
    queue: MetalCommandQueue,
    val windowContainer: WindowContainer,
    windowParams: Window.WindowParams,
) : SkikoWindow(
    device,
    queue,
    windowParams,
) {

    companion object {
        fun createWindow(
            device: MetalDevice,
            queue: MetalCommandQueue,
            title: String,
            origin: LogicalPoint,
            useCustomTitlebar: Boolean,
        ): RotatingBallWindow {
            val windowSize = LogicalSize(640.0, 480.0)
            val windowContentSize = windowSize // todo it's incorrect
            val container = WindowContainer.create(windowContentSize, useCustomTitlebar)

            val windowParams = Window.WindowParams(
                size = windowSize,
                title = title,
                origin = origin,
                titlebarConfiguration = if (useCustomTitlebar) {
                    TitlebarConfiguration.Custom(titlebarHeight = container.customTitlebar?.size?.height ?: 0.0)
                } else {
                    TitlebarConfiguration.Regular
                },
            )

            return RotatingBallWindow(device, queue, container, windowParams)
        }
    }

    init {
        windowContainer.resize(view.size().toLogical(window.scaleFactor()))
        performDrawing(syncWithCA = true)
    }

    override fun handleEvent(event: Event): EventHandlerResult {
        return if (super.handleEvent(event) == EventHandlerResult.Continue) {
            when {
                event is Event.WindowResize -> {
                    val viewSize = view.size().toLogical(window.scaleFactor())
                    assert(event.size == viewSize)
                    windowContainer.resize(event.size)
                    EventHandlerResult.Stop
                }
            }
            windowContainer.customTitlebar?.startWindowDrag = {
                window.startDrag()
            }
            windowContainer.customTitlebar?.zoomBoxClicked = {
                window.toggleMaximize()
            }
            windowContainer.handleEvent(event)
        } else {
            EventHandlerResult.Stop
        }
    }

    override fun Canvas.draw(size: PhysicalSize, time: Long) {
        val canvas = this
        // canvas.clear(Color.RED) // use RED to debug
        windowContainer.draw(canvas, time, window.scaleFactor())
    }
}

class ApplicationState : AutoCloseable {
    private val windows = mutableListOf<RotatingBallWindow>()

    private val device: MetalDevice by lazy {
        MetalDevice.create()
    }

    private val queue by lazy {
        MetalCommandQueue.create(device)
    }

    fun createWindow(useCustomTitlebar: Boolean) {
        val window = RotatingBallWindow.createWindow(
            device,
            queue,
            "Window ${windows.count()}",
            LogicalPoint(0.0, 0.0),
            useCustomTitlebar,
        )
        window.window.registerForDraggedTypes(listOf(Pasteboard.URL_TYPE))
        windows.add(window)
    }

    private fun setPaused(value: Boolean) {
        mainWindow()?.displayLink?.setRunning(!value)
    }

    private fun mainWindow(): RotatingBallWindow? {
        return windows.find { window ->
            window.window.isMain
        }
    }

    private fun changeCurrentWindowSize(delta: LogicalPixels) {
        mainWindow()?.let { window ->
            val currentOrigin = window.window.origin
            val currentSize = window.window.size
            // todo check display bounds
            // todo check min and max size
            window.window.setRect(
                origin = LogicalPoint(
                    currentOrigin.x - delta / 2.0,
                    currentOrigin.y - delta / 2.0,
                ),
                size = LogicalSize(
                    currentSize.width + delta,
                    currentSize.height + delta,
                ),
                animateTransition = true,
            )
        }
    }

    private fun changeTitlebarHeight(delta: LogicalPixels) {
        mainWindow()?.let { window ->
            window.windowContainer.customTitlebar?.let { currentTitlebar ->
                val size = currentTitlebar.size
                val newSize = LogicalSize(width = size.width, height = size.height + delta)
                currentTitlebar.size = newSize
                window.window.setTitlebarConfiguration(configuration = TitlebarConfiguration.Custom(newSize.height))
                window.windowContainer.resize(window.window.size)
            }
        }
    }

    private fun setCustomTitlebarEnabled(enabled: Boolean) {
        mainWindow()?.let { window ->
            if (enabled) {
                val configuration = TitlebarConfiguration.Custom(CustomTitlebar.DEFAULT_CUSTOM_TITLEBAR_HEIGHT)
                window.windowContainer.customTitlebar = CustomTitlebar(
                    LogicalPoint.Zero,
                    LogicalSize(width = window.window.contentSize.width, height = CustomTitlebar.DEFAULT_CUSTOM_TITLEBAR_HEIGHT),
                )
                window.window.setTitlebarConfiguration(configuration = configuration)
                window.windowContainer.resize(window.window.size)
            } else {
                val configuration = TitlebarConfiguration.Regular
                window.windowContainer.customTitlebar = null
                window.window.setTitlebarConfiguration(configuration = configuration)
                window.windowContainer.resize(window.window.size)
            }
        }
    }

    private fun makeWindowTransparent() {
        mainWindow()?.let { window ->
            window.window.setBackground(WindowBackground.Transparent)
        }
    }

    private fun toggleMiniaturizationOfFirstWindow() {
        windows.firstOrNull()?.window?.let { window ->
            if (window.isMiniaturized) {
                window.deminiaturize()
            } else {
                window.miniaturize()
            }
        }
    }

    private fun makeWindowOpaque() {
        mainWindow()?.let { window ->
            window.window.setBackground(WindowBackground.SolidColor(org.jetbrains.desktop.macos.Color(1.0, 1.0, 1.0, 1.0)))
        }
    }

    private var cursors = generateSequence { Cursor.Icon.entries.asSequence() }.flatten().iterator()

    private fun cycleCursor() {
        Cursor.icon = cursors.next()
    }

    private var effect = generateSequence { WindowVisualEffect.entries.asSequence() }.flatten().iterator()

    private fun cycleWindowEffects() {
        mainWindow()?.let { window ->
            window.window.setBackground(WindowBackground.VisualEffect(effect.next()))
        }
    }

    private fun killWindow(window: RotatingBallWindow) {
        windows.remove(window)
        window.window.unregisterDraggedTypes()
        window.close()
    }

    private fun logEvents(event: Event) {
        when (event) {
            is Event.KeyDown -> {
                Logger.info { "$event" }
            }

            is Event.KeyUp -> {
                Logger.info { "$event" }
            }

            is Event.ModifiersChanged -> {
                Logger.info { "$event" }
            }

            is Event.MouseEntered -> {
                Logger.info { "$event" }
            }

            is Event.MouseExited -> {
                Logger.info { "$event" }
            }

            is Event.MouseMoved -> {
                Logger.info { "$event" }
            }

            is Event.MouseDragged -> {
                Logger.info { "$event" }
            }

            is Event.MouseDown -> {
                Logger.info { "$event" }
            }

            is Event.MouseUp -> {
                Logger.info { "$event" }
            }

            else -> {}
        }
    }

    var lockedWindowId: WindowId? = null

    fun handleEvent(event: Event): EventHandlerResult {
//        logEvents(event)

        val result = when (event) {
            is Event.WindowCloseRequest -> {
                windows.find {
                    it.window.windowId() == event.windowId
                }?.let { window ->
                    killWindow(window)
                } ?: run {
                    Logger.warn { "Can't find window for $event" }
                }
                EventHandlerResult.Stop
            }

            is Event.WindowFullScreenToggle -> {
                AppMenuManager.setMainMenu(buildMenu())
                EventHandlerResult.Continue
            }

            else -> EventHandlerResult.Continue
        }
        return if (result == EventHandlerResult.Continue && event is WindowEvent) {
            val window = windows.find {
                it.window.windowId() == event.windowId
            }
            window?.let {
                window.handleEvent(event)
            } ?: EventHandlerResult.Continue
        } else {
            EventHandlerResult.Stop
        }
    }

    var nextNotificationId = 0

    fun buildMenu(): AppMenuStructure {
        return AppMenuStructure(
            AppMenuItem.SubMenu(
                // Ignored
                title = "App",
                AppMenuItem.Action(
                    "New Window",
                    keystroke = Keystroke(key = "n", modifiers = KeyModifiersSet.create(command = true)),
                    perform = { createWindow(useCustomTitlebar = true) },
                ),
                AppMenuItem.Action(
                    "Open...",
                    keystroke = Keystroke(key = "o", modifiers = KeyModifiersSet.create(command = true, shift = true)),
                    perform = {
                        val path = FileDialog.showOpenFileDialog(
                            params = FileDialog.CommonDialogParams(
                                title = "Hello Dialog",
                                prompt = "Acceptable",
                                directoryUrl = "/Users/pavel.sergeev/AndroidStudioProjects",
                            ),
                            openDialogParams = FileDialog.OpenDialogParams(allowsMultipleSelections = true),
                        )
                        println("openFileDialog callback received $path")
                    },
                ),
                AppMenuItem.Action(
                    "New Titled Window",
                    keystroke = Keystroke(key = "n", modifiers = KeyModifiersSet.create(command = true, shift = true)),
                    perform = { createWindow(useCustomTitlebar = false) },
                ),
                AppMenuItem.Separator,
                AppMenuItem.Action(
                    "Hide ${Application.name}",
                    keystroke = Keystroke(key = "h", modifiers = KeyModifiersSet.create(command = true)),
                    perform = {
                        Application.hide()
                    },
                ),
                AppMenuItem.Action(
                    "Hide Others",
                    keystroke = Keystroke(key = "h", modifiers = KeyModifiersSet.create(command = true, option = true)),
                    perform = {
                        Application.hideOtherApplications()
                    },
                ),
                AppMenuItem.Action(
                    "Show All",
                    perform = {
                        Application.unhideAllApplications()
                    },
                ),
                AppMenuItem.Action(
                    "Quit",
                    keystroke = Keystroke(key = "q", modifiers = KeyModifiersSet.create(command = true)),
                    perform = {
                        Application.stopEventLoop()
                    },
                ),
                specialTag = AppMenuItem.SubMenu.SpecialTag.AppNameMenu,
            ),
            AppMenuItem.SubMenu(
                title = "Edit",
                AppMenuItem.Action(
                    title = "Copy",
                    keystroke = Keystroke(key = "c", modifiers = KeyModifiersSet.create(command = true)),
                    specialTag = AppMenuItem.Action.SpecialTag.Copy,
                    perform = {
                        Pasteboard.clear()
                        val result = Pasteboard.writeObjects(Pasteboard.Item.of(Pasteboard.PNG_IMAGE_TYPE, jbIconBytes()))
                        Logger.info { "Pasteboard.writeObjects result: $result" }
                    },
                ),
                AppMenuItem.Action(
                    title = "Paste",
                    keystroke = Keystroke(key = "v", modifiers = KeyModifiersSet.create(command = true)),
                    specialTag = AppMenuItem.Action.SpecialTag.Paste,
                    perform = {
                        Logger.info {
                            Pasteboard.readItemsOfType(Pasteboard.STRING_TYPE).toString()
                                .replace("\n", "\\n")
                                .replace("\t", "\\t")
                                .replace("\r", "\\r")
                                .replace("\"", "\\\"")
                        }
                    },
                ),
                AppMenuItem.Action(
                    title = "Paste Files",
                    keystroke = Keystroke(key = "v", modifiers = KeyModifiersSet.create(command = true, shift = true)),
                    perform = {
                        Pasteboard.readFileItemPaths()
                    },
                ),
            ),
            AppMenuItem.SubMenu(
                title = "View",
                AppMenuItem.Action(
                    title = "Set Title",
                    keystroke = Keystroke(key = "s", modifiers = KeyModifiersSet.create(command = true)),
                    perform = {
                        mainWindow()?.window?.let { window ->
                            val previousTitle = window.title
                            Logger.info { "Title was: $previousTitle" }
                            window.title = "$previousTitle[x]"
                        }
                    },
                ),
                AppMenuItem.Action(
                    title = "Toggle Full Screen",
                    state = when (mainWindow()?.window?.isFullScreen) {
                        true -> AppMenuItem.ActionItemState.On
                        else -> AppMenuItem.ActionItemState.Off
                    },
                    keystroke = Keystroke(key = "f", modifiers = KeyModifiersSet.create(command = true, control = true)),
                    perform = { mainWindow()?.window?.toggleFullScreen() },
                ),
//                specialTag = "View",
            ),
            AppMenuItem.SubMenu(
                title = "Animation",
                AppMenuItem.Action(
                    title = "Pause",
                    keystroke = Keystroke(key = "p", modifiers = KeyModifiersSet.create(command = true)),
                    perform = { setPaused(true) },
                ),
                AppMenuItem.Action(
                    title = "Run",
                    keystroke = Keystroke(key = "r", modifiers = KeyModifiersSet.create(command = true)),
                    perform = { setPaused(false) },
                ),
            ),
            AppMenuItem.SubMenu(
                title = "Displays",
                AppMenuItem.Action(
                    title = "List Displays",
                    keystroke = Keystroke(key = "d", modifiers = KeyModifiersSet.create(command = true)),
                    perform = { Logger.info { Screen.allScreens().toString() } },
                ),
            ),
            AppMenuItem.SubMenu(
                title = "Window",
                AppMenuItem.Action(
                    title = "Increase Size",
                    keystroke = Keystroke(key = "+", modifiers = KeyModifiersSet.create(command = true)),
                    perform = { changeCurrentWindowSize(50.0) },
                ),
                AppMenuItem.Action(
                    title = "Decrease Size",
                    keystroke = Keystroke(key = "-", modifiers = KeyModifiersSet.create(command = true)),
                    perform = { changeCurrentWindowSize(-50.0) },
                ),
                AppMenuItem.SubMenu(
                    title = "Titlebar",
                    AppMenuItem.Action(
                        title = "Increate Titlebar Height",
                        keystroke = Keystroke(key = "+", modifiers = KeyModifiersSet.create(option = true, command = true)),
                        perform = {
                            changeTitlebarHeight(2.0)
                        },
                    ),
                    AppMenuItem.Action(
                        title = "Decrease Titlebar Height",
                        keystroke = Keystroke(key = "-", modifiers = KeyModifiersSet.create(option = true, command = true)),
                        perform = {
                            changeTitlebarHeight(-2.0)
                        },
                    ),
                    AppMenuItem.Action(
                        title = "Switch to Custom Titlebar",
                        perform = { setCustomTitlebarEnabled(true) },
                    ),
                    AppMenuItem.Action(
                        title = "Switch to Regular Titlebar",
                        perform = { setCustomTitlebarEnabled(false) },
                    ),
                ),
                AppMenuItem.Action(
                    title = "Toggle Miniaturize first Window",
                    keystroke = Keystroke(key = "m", modifiers = KeyModifiersSet.create(command = true, option = true)),
                    perform = { toggleMiniaturizationOfFirstWindow() },
                ),
                AppMenuItem.Action(
                    title = "Make Window Transparent",
                    keystroke = Keystroke(key = "t", modifiers = KeyModifiersSet.create(command = true)),
                    perform = { makeWindowTransparent() },
                ),
                AppMenuItem.Action(
                    title = "Make Window Opaque",
                    keystroke = Keystroke(key = "o", modifiers = KeyModifiersSet.create(command = true)),
                    perform = { makeWindowOpaque() },
                ),
                AppMenuItem.Action(
                    title = "Cycle Window Effects",
                    keystroke = Keystroke(key = "e", modifiers = KeyModifiersSet.create(command = true)),
                    perform = { cycleWindowEffects() },
                ),
                AppMenuItem.Action(
                    title = "Log Window Position",
                    keystroke = Keystroke(key = "l", modifiers = KeyModifiersSet.create(command = true)),
                    perform = {
                        mainWindow()?.window?.let { window ->
                            Logger.info {
                                """
                                    WindowId: ${window.windowId()}
                                    origin: ${window.origin}
                                    size: ${window.size}
                                    contentOrigin: ${window.contentOrigin}
                                    contentSize: ${window.contentSize}
                                """.trimIndent()
                            }
                        }
                    },
                ),
                AppMenuItem.Action(
                    title = "Lock Window",
                    keystroke = Keystroke(key = "L", modifiers = KeyModifiersSet.create(command = true, shift = true)),
                    perform = {
                        if (lockedWindowId == null) {
                            lockedWindowId = mainWindow()?.window?.windowId()
                        } else {
                            lockedWindowId = null
                        }
                    },
                ),
                AppMenuItem.Action(
                    title = "Close Window",
                    keystroke = Keystroke(key = "w", modifiers = KeyModifiersSet.create(command = true)),
                    perform = {
                        mainWindow()?.let {
                            killWindow(it)
                        }
                    },
                ),
                specialTag = AppMenuItem.SubMenu.SpecialTag.Window,
            ),
            AppMenuItem.SubMenu(
                title = "Mouse",
                AppMenuItem.Action(
                    title = "Cycle Mouse Cursor",
                    keystroke = Keystroke(key = "m", modifiers = KeyModifiersSet.create(command = true)),
                    perform = { cycleCursor() },
                ),
                AppMenuItem.Action(
                    title = "Mouse Toggle Cursor",
                    keystroke = Keystroke(key = "h", modifiers = KeyModifiersSet.create(command = true)),
                    perform = {
                        Cursor.hidden = !Cursor.hidden
                    },
                ),
            ),
            AppMenuItem.SubMenu(
                title = "Notifications",
                AppMenuItem.Action(
                    title = "Request Permission",
                    perform = {
                        NotificationCenter.requestAuthorization { granted ->
                            Logger.info { "Notification permission ${if (granted) "granted" else "denied"}" }
                        }
                    },
                ),
                AppMenuItem.Action(
                    title = "Log Notification Capabilities",
                    perform = {
                        NotificationCenter.getAuthorizationStatus { status ->
                            Logger.info { "Notification authorization status: $status" }
                        }
                    },
                ),
                AppMenuItem.Action(
                    title = "Show Test Notification",
                    keystroke = Keystroke(key = "n", modifiers = KeyModifiersSet.create(command = true, option = true)),
                    perform = {
                        thread {
                            sleep(5000)
                            GrandCentralDispatch.dispatchOnMain {
                                NotificationCenter.showNotification(
                                    title = "Test Notification",
                                    body = "This is a test notification from Kotlin Desktop Toolkit!",
                                    notificationId = NotificationCenter.NotificationId("TestNotification${nextNotificationId++}"),
                                ) { error ->
                                    if (error != null) {
                                        Logger.error { "Failed to show notification: $error" }
                                    } else {
                                        Logger.info { "Notification delivered successfully" }
                                    }
                                }
                            }
                        }
                    },
                ),
                AppMenuItem.Action(
                    title = "Show Notification with Default Sound",
                    perform = {
                        thread {
                            sleep(5000)
                            GrandCentralDispatch.dispatchOnMain {
                                NotificationCenter.showNotification(
                                    title = "Default Sound Notification",
                                    body = "This notification uses the default system sound.",
                                    sound = NotificationSound.Default,
                                    notificationId = NotificationCenter.NotificationId("DefaultSoundNotification${nextNotificationId++}"),
                                ) { error ->
                                    if (error != null) {
                                        Logger.error { "Failed to show notification with default sound: $error" }
                                    } else {
                                        Logger.info { "Default sound notification delivered successfully" }
                                    }
                                }
                            }
                        }
                    },
                ),
                AppMenuItem.Action(
                    title = "Show Silent Notification",
                    perform = {
                        thread {
                            sleep(5000)
                            GrandCentralDispatch.dispatchOnMain {
                                NotificationCenter.showNotification(
                                    title = "Silent Notification",
                                    body = "This notification has no sound.",
                                    sound = NotificationSound.None,
                                    notificationId = NotificationCenter.NotificationId("SilentNotification${nextNotificationId++}"),
                                ) { error ->
                                    if (error != null) {
                                        Logger.error { "Failed to show silent notification: $error" }
                                    } else {
                                        Logger.info { "Silent notification delivered successfully" }
                                    }
                                }
                            }
                        }
                    },
                ),
                AppMenuItem.Action(
                    title = "Show Critical Alert Notification",
                    perform = {
                        thread {
                            sleep(5000)
                            GrandCentralDispatch.dispatchOnMain {
                                NotificationCenter.showNotification(
                                    title = "Critical Alert",
                                    body = "This is a critical alert that bypasses Do Not Disturb and mute switch.",
                                    sound = NotificationSound.Critical,
                                    notificationId = NotificationCenter.NotificationId("CriticalAlertNotification${nextNotificationId++}"),
                                ) { error ->
                                    if (error != null) {
                                        Logger.error { "Failed to show critical notification: $error" }
                                    } else {
                                        Logger.info { "Critical notification delivered successfully" }
                                    }
                                }
                            }
                        }
                    },
                ),
                AppMenuItem.Action(
                    title = "Show Ringtone Notification",
                    perform = {
                        thread {
                            sleep(5000)
                            GrandCentralDispatch.dispatchOnMain {
                                NotificationCenter.showNotification(
                                    title = "Ringtone Notification",
                                    body = "This notification uses the default ringtone sound.",
                                    sound = NotificationSound.Ringtone,
                                    notificationId = NotificationCenter.NotificationId("RingtoneNotification${nextNotificationId++}"),
                                ) { error ->
                                    if (error != null) {
                                        Logger.error { "Failed to show ringtone notification: $error" }
                                    } else {
                                        Logger.info { "Ringtone notification delivered successfully" }
                                    }
                                }
                            }
                        }
                    },
                ),
                AppMenuItem.Action(
                    title = "Show Named Sound Notification (Basso)",
                    perform = {
                        thread {
                            sleep(5000)
                            GrandCentralDispatch.dispatchOnMain {
                                NotificationCenter.showNotification(
                                    title = "Named Sound Notification",
                                    body = "This notification uses the Basso system sound.",
                                    sound = NotificationSound.Named("Basso.aiff"),
                                    notificationId = NotificationCenter.NotificationId("NamedSoundNotification${nextNotificationId++}"),
                                ) { error ->
                                    if (error != null) {
                                        Logger.error { "Failed to show named sound notification: $error" }
                                    } else {
                                        Logger.info { "Named sound notification delivered successfully" }
                                    }
                                }
                            }
                        }
                    },
                ),
                AppMenuItem.Action(
                    title = "Show Critical Named Sound Notification (Funk)",
                    perform = {
                        thread {
                            sleep(5000)
                            GrandCentralDispatch.dispatchOnMain {
                                NotificationCenter.showNotification(
                                    title = "Critical Named Sound",
                                    body = "This notification uses the Funk system sound as a critical alert.",
                                    sound = NotificationSound.CriticalNamed("Funk.aiff"),
                                    notificationId = NotificationCenter.NotificationId(
                                        "CriticalNamedSoundNotification${nextNotificationId++}",
                                    ),
                                ) { error ->
                                    if (error != null) {
                                        Logger.error { "Failed to show critical named sound notification: $error" }
                                    } else {
                                        Logger.info { "Critical named sound notification delivered successfully" }
                                    }
                                }
                            }
                        }
                    },
                ),
                AppMenuItem.Separator,
                AppMenuItem.Action(
                    title = "Show Notification with Action Buttons",
                    keystroke = Keystroke(key = "a", modifiers = KeyModifiersSet.create(command = true, option = true)),
                    perform = {
                        thread {
                            sleep(5000)
                            GrandCentralDispatch.dispatchOnMain {
                                NotificationCenter.showNotification(
                                    title = "Action Required",
                                    body = "Please choose an action:",
                                    sound = NotificationSound.Default,
                                    categoryId = NotificationCenter.CategoryId("action_category"),
                                    notificationId = NotificationCenter.NotificationId("TestNotification${nextNotificationId++}"),
                                ) { error ->
                                    if (error != null) {
                                        Logger.error { "Failed to show action notification: $error" }
                                    } else {
                                        Logger.info { "Action notification delivered successfully" }
                                    }
                                }
                            }
                        }
                    },
                ),
                AppMenuItem.Action(
                    title = "Show Evil Notification with Action Buttons",
                    perform = {
                        thread {
                            sleep(5000)
                            GrandCentralDispatch.dispatchOnMain {
                                NotificationCenter.showNotification(
                                    title = "Evil Action Required",
                                    body = "Please choose an action:",
                                    sound = NotificationSound.Default,
                                    notificationId = NotificationCenter.NotificationId("EvilTestNotification${nextNotificationId++}"),
                                    categoryId = NotificationCenter.CategoryId("evil_action_category"),
                                ) { error ->
                                    if (error != null) {
                                        Logger.error { "Failed to show action notification: $error" }
                                    } else {
                                        Logger.info { "Action notification delivered successfully" }
                                    }
                                }
                            }
                        }
                    },
                ),
            ),
            AppMenuItem.SubMenu(title = "Help"),
        )
    }

    override fun close() {
        windows.forEach {
            it.close()
        }
        queue.close()
        device.close()
    }
}

fun dragAndDropCallback(): DragAndDropCallbacks {
    return object : DragAndDropCallbacks {
        override fun onDragEntered(info: DragInfo): DragOperation {
            val files = Pasteboard.readFileItemPaths(pasteboardName = info.pasteboardName)
            println("Drag Entered: $info, files: $files")
            return DragOperation.COPY
        }

        override fun onDragUpdated(info: DragInfo): DragOperation {
            val files = Pasteboard.readFileItemPaths(pasteboardName = info.pasteboardName)
            println("Drag Updated: $info, files: $files")
            return DragOperation.COPY
        }

        override fun onDragExited(info: DragInfo?) {
            val files = info?.pasteboardName?.let { Pasteboard.readFileItemPaths(pasteboardName = it) }
            println("Drag Exited: $info files: $files")
        }

        override fun onDragPerformed(info: DragInfo): Boolean {
            val files = Pasteboard.readFileItemPaths(pasteboardName = info.pasteboardName)
            println("Drag Performed: $info files: $files")
            return true
        }
    }
}

fun jbIconBytes(): ByteArray {
    return object {}.javaClass.getResource("/jb-logo.png")!!.readBytes()
}

fun setupNotificationCategories() {
    if (NotificationCenter.isSupportedByApplication) {
        NotificationCenter.registerNotificationCategories(
            listOf(
                NotificationCategory(
                    categoryId = NotificationCenter.CategoryId("action_category"),
                    actions = listOf(
                        NotificationAction(
                            actionId = NotificationCenter.ActionId("approve"),
                            title = "Approve",
                        ),
                        NotificationAction(
                            actionId = NotificationCenter.ActionId("deny"),
                            title = "Deny",
                        ),
                        NotificationAction(
                            actionId = NotificationCenter.ActionId("later"),
                            title = "Remind Me Later",
                        ),
                    ),
                ),
                NotificationCategory(
                    categoryId = NotificationCenter.CategoryId("evil_action_category"),
                    actions = listOf(
                        NotificationAction(
                            actionId = NotificationCenter.ActionId("not approve"),
                            title = "Not Approve",
                            isForeground = false,
                            requiresAuthentication = true,

                        ),
                        NotificationAction(
                            actionId = NotificationCenter.ActionId("not deny"),
                            title = "Not Deny",
                            isDestructive = true,
                        ),
                        NotificationAction(
                            actionId = NotificationCenter.ActionId("not later"),
                            title = "NotRemind Me Later",
                            isDestructive = true,
                        ),
                    ),
                ),
            ),
        )
        NotificationCenter.setActionResponseCallback { notificationId, actionId ->
            Logger.info { "Notification action: $notificationId, actionId: $actionId" }
        }
    }
}

fun main() {
    KotlinDesktopToolkit.init(consoleLogLevel = LogLevel.Info)
    Logger.info { runtimeInfo() }
    GrandCentralDispatch.startOnMainThread {
        Application.init(Application.ApplicationConfig())
        DragAndDropHandler.init(dragAndDropCallback())
        ApplicationState().use { state ->
            state.createWindow(useCustomTitlebar = true)
            Application.runEventLoop { event ->
                if (event is Event.ApplicationDidFinishLaunching) {
                    setupNotificationCategories()
                    Application.setDockIcon(jbIconBytes())
                    AppMenuManager.setMainMenu(state.buildMenu())
                }
                state.handleEvent(event)
            }
        }
        DragAndDropHandler.close()
        NotificationCenter.close()
        GrandCentralDispatch.close()
    }
}
